extern crate chrono;

#[macro_use]
extern crate clap;
extern crate toml;
use colog;
// use log;
mod text;
// use serde_derive::Deserialize;
// use serde::Deserialize;
use text::text_read as textlib;
use text::db as dblib;
// use std::env;
use std::time::SystemTime;
use std::path::Path;
use std::io::Read;
use std::io::Write;
// use std::io::prelude::*;
use std::fs::File;
use toml::Value;

use mongodb::{doc,bson};
use mongodb::{Client,ThreadedClient};
use mongodb::db::ThreadedDatabase;

use std::process::exit;

use chrono::Local;

fn log_init(){
    let mut clog = colog::builder();
    clog.format(|buf, record| {
        writeln!(buf,
            "{} [{}] - {}",
            Local::now().format("%Y-%m-%dT%H:%M:%S"),
            record.level(),
            record.args()
        )
    });
    // clog.filter(None, log::LevelFilter::Warn);
    clog.init();
}



#[derive(Clap)]
#[clap(version = "1.3", author = "Qingluan")]
struct Opts {
    #[clap(short = "c", long = "config", default_value = "~/.config/nlp-search.toml")]
    config: String,
    
    #[clap(short = "r", long = "root", default_value = "[None]")]
    root: String,

    #[clap(short = "d", long = "output", default_value = "[None]")]
    dst: String,

    #[clap(long="clean")]
    clear_db:bool,
    
    #[clap(short = "t", long = "thread", default_value = "1")]
    thread: i32,
}



macro_rules! try_it {
    ($some:expr) => {
        $some.expect("some error !");
    };
}


fn clean_db(){
    let client = Client::connect("localhost", 27017).expect("Failed to initialize client.");
    let db = client.db("sbas");
    let coll = db.collection("meta");
    match coll.delete_many(doc!{
            "info_type" : "record"
        }, None){
        Ok(_) => {},
        Err(e) => {println!("{}",e)}
    }
}

fn _f(p: &str) -> String{
    if p.contains("\""){
        p.replace("\"", "")
    }else{
        p.to_string()
    }
}

fn read_config(path:&str) -> Result<Value, &'static str>{
    
    let path = Path::new(path);
    let mut buf = String::new();
    let mut file = File::open(&path).expect("read toml config failed");
    match file.read_to_string(&mut buf){
        Ok(_) => {
            if let Ok(value) = buf.parse::<Value>(){
                println!("{}", _f(value["service"]["tmp_dir"].as_str().unwrap())); 
                return Ok(value);
            }
            return Err("read txt failed");
        },
        _ => {
            return Err("read txt failed");
        }
    }
    
}


fn main(){
    log_init();
    let opts: Opts = Opts::parse();
    if opts.clear_db{
        clean_db();
        exit(0);
    }

    let mut config = "a = 'b'".parse::<Value>().expect("test error");
    if opts.root == "[None]"{
        config = read_config(&opts.config).expect("read config error");
    }

    let (tx, rx) = textlib::get_channels();
    let tx_back = tx.clone();
    let mut scan_dirs:Vec<String> =  vec![];
    if opts.root != "[None]" {
        scan_dirs.push(opts.root);
    }else{
        if let Some(array) = config["service"]["scan_paths"].as_array(){
            let _ = array.iter().map(|dir_path|{
                println!("path to search : {}", try_it!(dir_path.as_str()));
                scan_dirs.push(dir_path.to_string());
            }).collect::<()>();
        };
    }


    let (_, pool,deleted_keys) = textlib::Text::scan(scan_dirs, tx_back, Some(opts.thread));

    let mut dir_path_string = opts.dst.clone();
    if opts.dst == "[None]" {
        dir_path_string =_f(try_it!(config["service"]["tmp_dir"].as_str()));
    }

    // println!("tmp : {}", dir_path_string );
    if deleted_keys.len() > 0{
        let dir_path = Path::new(&dir_path_string);
        let file_end = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs().to_string() + ".deleted";
        
        // let file_end = time_end.as_secs().to_string() + ".deleted";
        let path = dir_path.join(file_end).to_str().unwrap().to_string();
        match textlib::to_line_file(&deleted_keys, Some(&path)){
            Ok(_) => {
                println!("get some deleted_keys to file:{}", &path);
            },
            Err(_) =>{}
        };
    }

    dblib::observer_to_save(&dir_path_string, rx);
    &pool.join();
}
